
using System;
using System.Drawing;
using System.Windows.Forms;
using System.Collections;

namespace System.Ups.Windows.Forms {
  /// <summary>
  /// Summary description for MonitorMeter.
  /// </summary>
  public class MonitorMeter : Control {
    private int barValue = 25;
    private int barCount = 0;
    private Rectangle rectValue;
    private Rectangle rectBarContainer;
    private Rectangle rectBar;
    private int barHeight = 4;
    private int barPadding = 8;
    private bool showTitle;
    private Rectangle rectTitle = Rectangle.Empty;

    /// <summary>
    /// Initializes a new instance of the <see cref="MonitorMeter"/> class.
    /// </summary>
    public MonitorMeter() {
      SetStyle(ControlStyles.UserPaint | ControlStyles.ResizeRedraw | ControlStyles.DoubleBuffer | ControlStyles.AllPaintingInWmPaint, true);
    }


    /// <summary>
    /// Raises the <see cref="E:System.Windows.Forms.Control.Resize"></see> event.
    /// </summary>
    /// <param name="e">An <see cref="T:System.EventArgs"></see> that contains the event data.</param>
    protected override void OnResize(EventArgs e) {
      base.OnResize (e);
    }

    /// <summary>
    /// Raises the <see cref="E:System.Windows.Forms.Control.Paint"></see> event.
    /// </summary>
    /// <param name="e">A <see cref="T:System.Windows.Forms.PaintEventArgs"></see> that contains the event data.</param>
    protected override void OnPaint(PaintEventArgs e) {
      Graphics g = e.Graphics;
      g.Clear(this.BackColor);

      if ( this.BackgroundImage != null )
        g.DrawImage(this.BackgroundImage,this.ClientRectangle);

      StringFormat sf = new StringFormat();
      sf.Alignment = StringAlignment.Center;
      sf.LineAlignment = StringAlignment.Near;

      SizeF sizeF = g.MeasureString("0",this.Font,this.rectValue.Width,sf);
      if ( showTitle )
        this.rectTitle = new Rectangle(0,0,this.ClientRectangle.Width,(int)sizeF.Height + 2);
      else
        this.rectTitle = Rectangle.Empty;

      this.rectBarContainer = new Rectangle(this.barPadding,rectTitle == Rectangle.Empty?4:rectTitle.Bottom + 4,this.Width - (this.barPadding*2),this.Height - rectTitle.Bottom - ((int)sizeF.Height+10));
      rectBar = new Rectangle(barPadding,rectBarContainer.Bottom - 10,rectBarContainer.Width,barHeight);
      barCount = ( rectBarContainer.Height / (rectBar.Height + 4) );
      this.rectValue = new Rectangle(0,rectBarContainer.Bottom + 2,this.Width,this.Height - rectBarContainer.Bottom - 2);

      if ( this.showTitle )
        drawTitle(g);
      drawBars(g);
      drawValue(g);
    }

    /// <summary>
    /// Draws the title.
    /// </summary>
    /// <param name="g">The g.</param>
    private void drawTitle(Graphics g) {
      StringFormat sf = new StringFormat();
      sf.Alignment = StringAlignment.Center;
      sf.LineAlignment = StringAlignment.Near;
      g.DrawString(this.Text,this.Font,new SolidBrush(this.ForeColor),this.rectTitle,sf);
    }

    /// <summary>
    /// Draws the value.
    /// </summary>
    /// <param name="g">The g.</param>
    private void drawValue(Graphics g) {
      StringFormat sf = new StringFormat();
      sf.Alignment = StringAlignment.Center;
      sf.LineAlignment = StringAlignment.Near;
      g.DrawString(string.Format("{0}%", this.barValue),this.Font,new SolidBrush(this.ForeColor),this.rectValue,sf);
    }

    /// <summary>
    /// Draws the bars.
    /// </summary>
    /// <param name="g">The g.</param>
    private void drawBars(Graphics g) {
      barCount = ( rectBarContainer.Height / (rectBar.Height + 4) );
      int bCount = 0;
      while ( rectBar.Top > rectBarContainer.Top ) {
        if ( Percent(this.barValue,barCount) >  bCount )
          drawActiveBar(g);
        else
          drawInactiveBar(g);
        int x = ((rectBar.Width+(barPadding*2)) / 2);
        rectBar.Offset(0,-(rectBar.Height+4) );
        bCount ++;
      }
      Rectangle rect = new Rectangle(((rectBar.Width / 2)-3) + this.rectBar.Left,rectBarContainer.Top,6,this.rectBarContainer.Height);

      if ( this.BackgroundImage == Color.Empty ) {
        g.FillRectangle ( new SolidBrush ( this.BackColor ), rect );
      } else {
        g.FillRectangle ( new SolidBrush ( this.BackColor ), rect );
        g.DrawImage ( this.BackgroundImage, rect, rect, GraphicsUnit.Pixel );
      }
      rectBar = new Rectangle(barPadding,rectBarContainer.Bottom - 10,this.Width - (barPadding*2),barHeight);
    }

    /// <summary>
    /// Percents the specified a.
    /// </summary>
    /// <param name="a">A.</param>
    /// <param name="b">The b.</param>
    /// <returns></returns>
    private int Percent(int a, int b) {
      try {
        if ( b == 0 || a == 0 ) 
          return 0;
        double x = (double)a / 100d;
        double ret = (x * b);
        return ret>0?ret<1?1:(int)ret:(int)ret;
      } catch { return a>0?1:0; }
    }

    /// <summary>
    /// Draws the active bar.
    /// </summary>
    /// <param name="g">The g.</param>
    private void drawActiveBar ( Graphics g ) {
      g.FillRectangle(new SolidBrush(this.ForeColor),rectBar);
    }

    /// <summary>
    /// Draws the inactive bar.
    /// </summary>
    /// <param name="g">The g.</param>
    private void drawInactiveBar ( Graphics g ) {
      Color clr = Color.FromArgb(100,this.ForeColor);
      g.FillRectangle(new SolidBrush(clr),rectBar);
    }

    /// <summary>
    /// Gets the bar count.
    /// </summary>
    /// <value>The bar count.</value>
    public int BarCount { get { return this.barCount; } }
    /// <summary>
    /// Gets or sets the height of the bar.
    /// </summary>
    /// <value>The height of the bar.</value>
    public int BarHeight { get { return this.barHeight; } set { this.barHeight = value<2?2:value; this.Invalidate(); } }
    /// <summary>
    /// Gets or sets the value.
    /// </summary>
    /// <value>The value.</value>
    public int Value { get { return this.barValue; } set { this.barValue = value>100?100:value<0?0:value; this.Invalidate(); } }
    /// <summary>
    /// Gets or sets the bar padding.
    /// </summary>
    /// <value>The bar padding.</value>
    public int BarPadding { get { return this.barPadding; } set { this.barPadding = value>2?value:2; this.Invalidate(); } }
    /// <summary>
    /// Gets or sets a value indicating whether [show title].
    /// </summary>
    /// <value><c>true</c> if [show title]; otherwise, <c>false</c>.</value>
    public bool ShowTitle { 
      get { return this.showTitle; } 
      set { 
        if ( this.showTitle != value ) {
          this.showTitle = value; 
          this.Invalidate();
        }
      } 
    }
  }
}
